var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
var disco;
(function (disco) {
    /// <reference path="../typings/jquery/jquery.d.ts" />
    /// <reference path="../typings/knockout/knockout.d.ts" />
    /// <reference path="../disco/disco.ts" />
    (function (model) {
        var Metadata = (function () {
            function Metadata() {
            }
            return Metadata;
        })();
        model.Metadata = Metadata;

        var DataContext = (function () {
            function DataContext() {
                this.entityTypes = {};
                this.entitySets = {};
            }
            DataContext.prototype.entityType = function (name, value) {
                if (typeof value === "undefined") { value = undefined; }
                if (value)
                    return this.entityTypes[name] = value;
else
                    return this.entityTypes[name];
            };

            DataContext.prototype.entitySet = function (typeName, value) {
                if (typeof value === "undefined") { value = undefined; }
                if (value)
                    return this.entitySets[typeName] = value;
else
                    return this.entitySets[typeName];
            };
            return DataContext;
        })();
        model.DataContext = DataContext;

        var EntityType = (function () {
            function EntityType() {
                this.entitySet = null;
                this.properties = [];
                this.navigationProperties = [];
                this.baseType = null;
                this.metadata = null;
                this.useToStringForStringify = true;
            }
            EntityType.prototype.toString = function () {
                return '<' + this.name + '>';
            };
            return EntityType;
        })();
        model.EntityType = EntityType;

        var EntityProperty = (function () {
            function EntityProperty() {
                this.isInherited = false;
                this.isKey = false;
                this.isNullable = true;
                this.isMany = false;
                this.type = null;
                this.isNavigation = false;
                this.useToStringForStringify = true;
            }
            EntityProperty.prototype.toString = function () {
                return '<' + this.name + ': ' + this.typeString + '>';
            };
            return EntityProperty;
        })();
        model.EntityProperty = EntityProperty;

        var NavigationProperty = (function () {
            function NavigationProperty() {
                this.inherited = false;
            }
            return NavigationProperty;
        })();
        model.NavigationProperty = NavigationProperty;

        var AssociationEnd = (function () {
            function AssociationEnd() {
            }
            return AssociationEnd;
        })();
        model.AssociationEnd = AssociationEnd;

        var EntityCollection = (function () {
            function EntityCollection() {
                this.rows = ko.observableArray([]);
            }
            return EntityCollection;
        })();
        model.EntityCollection = EntityCollection;

        var EntitySet = (function (_super) {
            __extends(EntitySet, _super);
            function EntitySet() {
                _super.apply(this, arguments);
            }
            return EntitySet;
        })(EntityCollection);
        model.EntitySet = EntitySet;

        var Entity = (function () {
            function Entity() {
                this.properties = {};
                this.isEntity = true;
            }
            Entity.prototype.property = function (name, value) {
                if (typeof value === "undefined") { value = undefined; }
                if (value)
                    return this.properties[name] = value;
else
                    return this.properties[name];
            };
            return Entity;
        })();
        model.Entity = Entity;

        var PropertyValue = (function () {
            function PropertyValue() {
            }
            return PropertyValue;
        })();
        model.PropertyValue = PropertyValue;

        var OntologyModel = (function () {
            function OntologyModel() {
                this.dataContext = ko.observable();
            }
            OntologyModel.prototype.getDataContext = function () {
                return this.dataContext;
            };

            OntologyModel.prototype.load = function (serviceUri, success) {
                if (typeof success === "undefined") { success = function (o) {
                }; }
                var _this = this;
                OData.read(serviceUri + "$metadata", function (data, response) {
                    //success
                    var context = new DataContext();
                    context.serviceUri = serviceUri;
                    context.metadata = data;

                    var contextMgr = new model.helper.DataContextManager(context);
                    contextMgr.generateEntityModel();

                    _this.dataContext(context);
                    success(_this);
                }, function (error) {
                    //error
                    //TODO: error handling
                }, OData.metadataHandler);
            };
            return OntologyModel;
        })();
        model.OntologyModel = OntologyModel;

        var EntityTypeManager = (function () {
            function EntityTypeManager(entityType) {
                if (typeof entityType === "undefined") { entityType = null; }
                this.entityType = entityType;
            }
            EntityTypeManager.prototype.getEntityType = function () {
                return this.entityType;
            };
            EntityTypeManager.prototype.setEntityType = function (value) {
                if (typeof value === "undefined") { value = new EntityType(); }
                return this.entityType = value;
            };

            EntityTypeManager.prototype.each = function (types, callback) {
                var _this = this;
                types.forEach(function (type, index) {
                    _this.setEntityType(type);
                    callback.call(_this, _this, index, types);
                }, this);
            };

            EntityTypeManager.prototype.generateSimpleProperties = function (metaEntityType, associations) {
                var type = this.getEntityType();
                type.metadata = metaEntityType;
                type.name = metaEntityType.name;

                if (type.metadata.property) {
                    var keyPropertyName = type.metadata.key ? type.metadata.key.propertyRef.name : null;

                    var propertyMgr = new EntityPropertyManager();

                    type.metadata.property.forEach(function (metaProperty) {
                        propertyMgr.generateEntityProperty(metaProperty, keyPropertyName);
                        type.properties.push(propertyMgr.getEntityProperty());
                    });
                }
                if (type.metadata.navigationProperty) {
                    var navigationPropertyMgr = new NavigationPropertyManager();

                    type.metadata.navigationProperty.forEach(function (metaProperty) {
                        navigationPropertyMgr.generateNavigationProperty(metaProperty, associations);
                        type.navigationProperties.push(navigationPropertyMgr.getNavigationProperty());
                    });
                }
            };
            EntityTypeManager.prototype.applyBaseType = function (context) {
                var type = this.getEntityType();
                if (type && type.metadata.baseType && !type.baseType) {
                    var baseTypeName = disco.Namespace.remove(type.metadata.baseType);
                    type.baseType = context.entityType(baseTypeName);

                    new EntityTypeManager(type.baseType).applyBaseType(context);

                    var entityPropertyMgr = new EntityPropertyManager();
                    var inheritedEntityProperties = entityPropertyMgr.copyProperties(type.baseType.properties);
                    inheritedEntityProperties.forEach(function (property) {
                        property.isInherited = true;
                    });
                    type.properties = inheritedEntityProperties.concat(type.properties);

                    var navigationPropertyMgr = new NavigationPropertyManager();
                    var inheritedNavigationProperties = navigationPropertyMgr.copyProperties(type.baseType.navigationProperties);
                    inheritedNavigationProperties.forEach(function (property) {
                        property.inherited = true;
                    });
                    type.navigationProperties = inheritedNavigationProperties.concat(type.navigationProperties);
                }
            };
            EntityTypeManager.prototype.addNavigationToEntityProperties = function (context) {
                var entityPropertyMgr = new EntityPropertyManager();
                var type = this.getEntityType();

                var convertedProperties = type.navigationProperties.map(function (navigationProperty) {
                    return entityPropertyMgr.fromNavigationProperty(navigationProperty, context);
                });
                type.properties = type.properties.concat(convertedProperties);
            };
            return EntityTypeManager;
        })();
        model.EntityTypeManager = EntityTypeManager;

        var EntityPropertyManager = (function () {
            function EntityPropertyManager(entityProperty) {
                if (typeof entityProperty === "undefined") { entityProperty = null; }
                this.entityProperty = entityProperty;
            }
            EntityPropertyManager.prototype.getEntityProperty = function () {
                return this.entityProperty;
            };

            EntityPropertyManager.prototype.setEntityProperty = function (entityProperty) {
                if (typeof entityProperty === "undefined") { entityProperty = new EntityProperty(); }
                return this.entityProperty = entityProperty;
            };

            EntityPropertyManager.prototype.generateEntityProperty = function (metaProperty, keyPropertyName) {
                var property = this.setEntityProperty();
                property.name = metaProperty.name;
                property.typeString = metaProperty.type;
                property.isNullable = metaProperty.nullable || true;
                property.isKey = metaProperty.name == keyPropertyName;
            };

            EntityPropertyManager.prototype.fromNavigationProperty = function (navigationProperty, context) {
                var entityProperty = this.setEntityProperty();
                var associationEnd = navigationProperty.to;

                entityProperty.isNavigation = true;
                entityProperty.name = associationEnd.role;
                entityProperty.isMany = associationEnd.multiplicity == '*';
                entityProperty.isNullable = associationEnd.multiplicity != '1';
                entityProperty.typeString = disco.Namespace.remove(associationEnd.type);
                entityProperty.type = context.entityType(entityProperty.typeString);

                return entityProperty;
            };

            EntityPropertyManager.prototype.copyProperty = function (property) {
                var p = property;
                var ret = {
                    isInherited: p.isInherited,
                    isKey: p.isKey,
                    isMany: p.isMany,
                    isNavigation: p.isNavigation,
                    isNullable: p.isNullable,
                    name: p.name,
                    type: p.type,
                    typeString: p.typeString,
                    toString: EntityProperty.prototype.toString,
                    useToStringForStringify: p.useToStringForStringify
                };
                return this.setEntityProperty(ret);
            };
            EntityPropertyManager.prototype.copyProperties = function (properties) {
                var self = this;
                return properties.map(function (property) {
                    return self.copyProperty(property);
                });
            };
            return EntityPropertyManager;
        })();
        model.EntityPropertyManager = EntityPropertyManager;

        var NavigationPropertyManager = (function () {
            function NavigationPropertyManager(navigationProperty) {
                if (typeof navigationProperty === "undefined") { navigationProperty = null; }
                this.navigationProperty = navigationProperty;
            }
            NavigationPropertyManager.prototype.getNavigationProperty = function () {
                return this.navigationProperty;
            };

            NavigationPropertyManager.prototype.setNavigationProperty = function (property) {
                if (typeof property === "undefined") { property = new NavigationProperty(); }
                return this.navigationProperty = property;
            };

            NavigationPropertyManager.prototype.generateNavigationProperty = function (metaProperty, metaAssociations) {
                var property = this.setNavigationProperty();
                property.name = metaProperty.name;

                var metaAssociationName = disco.Namespace.remove(metaProperty.relationship);
                var metaAssociation = disco.Collections.single(metaAssociations, function (metaAssociation) {
                    return metaAssociationName == metaAssociation.name;
                });
                property.from = disco.Collections.single(metaAssociation.end, function (associationEnd) {
                    return metaProperty.fromRole == associationEnd.role;
                });
                property.to = disco.Collections.single(metaAssociation.end, function (associationEnd) {
                    return metaProperty.toRole == associationEnd.role;
                });
            };

            NavigationPropertyManager.prototype.copyProperty = function (property) {
                var p = property;
                var ret = {
                    from: p.from,
                    inherited: p.inherited,
                    name: p.name,
                    to: p.to
                };
                return this.setNavigationProperty(ret);
            };

            NavigationPropertyManager.prototype.copyProperties = function (properties) {
                var self = this;
                return properties.map(function (property) {
                    return self.copyProperty(property);
                });
            };
            return NavigationPropertyManager;
        })();
        model.NavigationPropertyManager = NavigationPropertyManager;

        model.stringifyReplacer = function (key, value) {
            var replacePropertyValue = function (val) {
                if (typeof val == 'object') {
                    if (val.isEntity) {
                        return '<' + val.type.name + ' #' + val.property('Id').value + '>';
                    } else if (disco.Type.get(val) == '[object Array]') {
                        return (val).map(replacePropertyValue);
                    } else
                        return val;
                } else
                    return val;
            };

            if (value && value.useToStringForStringify) {
                return value.toString();
            }
            if (key == 'value')
                return replacePropertyValue(value);
            return value;
        };
    })(disco.model || (disco.model = {}));
    var model = disco.model;
})(disco || (disco = {}));
